There are several methods you can use to mitigate against casual cheating. In my view you should not expect to be able to stop a determined cheater without a more formal validation process (cc authorization..etc).
The easiest approach is to ask for a residential address to send goods when they win :)
First and foremost deny the cheater any feedback channel to be able to tell if their submission was accepted or rejected. If there is a slight delay for accepted entries make sure you add a fake delay with some jitter so they can't tell if their scheme for thwarting your anti-cheating method worked or even if you have any anti-cheating methods at all. Detecting bulk submissions by a cheater are much easier when they don't feel they need to be creative.
IP Address as you mentioned. Perhaps use geoip, whois..etc to get distributions over time WRT area.
User agent and system fingerprinting - there is a huge amount of information you can get from the browser that may or may not be unique. Browser type, version, operating system, screen resolution, color depth, installed fonts, plugins (flash, pdf, java...etc) and associated version numbers, language, browsers local time (log client clock skew)
Use of cookies, perhaps hide references to an innocent sounding domain in an included javascript you also control. This may be used to correlate the manual deletion of obvious cookies with the hidden cookies. Its less known that cookies can also be stored in separate databases of other plugins the user may have such as flash player. These are NOT removed when the browser cookies are deleted.
Use of images with cache headers. The first time a user visits the site display an image after their entry is submitted. If they've already filled out the form and they submit again the image would be cached and you can use the absence of the image request to assume submitted entries are a result of cheating.